#include <unistd.h>

#include <iostream>
#include <memory>
#include <vector>
#include <functional>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <list>


#include<sys/socket.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

#include "nocopy.hpp"
class Processes : public nocopy
{
    struct proc_r{
        pid_t _id;
        int _readfd;
        int _writefd;
        proc_r(int id,int rfd,int wfd)
        :_id(id)
        ,_readfd(rfd)
        ,_writefd(wfd)
        {

        }
    };

    int _proc_num;
    std::list<proc_r> _proc;
    using func_t = std::function<void(int)> ;
    func_t _func;
    public:
    //默认子进程自动回收，不由父进程做回收
    //要求传入可变参数的第一个是函数指针,int 返回值的类型的
    void ERR_EXIT(const char*p)
    {
        std::cout<<p<<"\n";
    }
static const int CONTROL_LEN = CMSG_LEN(sizeof(int));

/**
 * @brief 发送目标文件描述符
 * @param fd		传递信息的 UNIX 域 文件描述符
 * @param fd_to_send	待发送的文件描述符
 */
void send_fd(int fd, int fd_to_send)
{
	struct iovec iov[1];
	struct msghdr msg;
	char buf[0];

	iov[0].iov_base = buf;
	iov[0].iov_len = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	cmsghdr cm;			/* 这是辅助数据头部结构体，文件描述符就是通过这个结构体以及后面的数据部分发送的 */
	cm.cmsg_len = CONTROL_LEN;	/* 辅助数据的字节数，包扩头部和真正的数据 */
	cm.cmsg_level = SOL_SOCKET;	/* 表示原始协议级别，与 setsockopt 的 level 参数相同 */
	cm.cmsg_type = SCM_RIGHTS;	/* 控制信息类型，SCM_RIGHTS 表示传送的内容是访问权 */
	*(int*)CMSG_DATA(&cm) = fd_to_send;/* 设置真正数据部分为我们想发送的文件描述符 */
	msg.msg_control = &cm;		/* 设置辅助数据 */
	msg.msg_controllen = CONTROL_LEN;

	int n = sendmsg(fd, &msg, 0);
    if(n==-1)
    {
        std::cout<<strerror(errno)<<"\n";
        exit(-1);
    }

}

/**
 * @brief 接受文件描述符
 * @param fd 传递信息的 UNIX 域 文件描述符
 */
int recv_fd(int fd)
{
	struct iovec iov[1];
	struct msghdr msg;
	char buf[0];

	iov[0].iov_base = buf;
	iov[0].iov_len = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	cmsghdr cm;
	msg.msg_control = &cm;
	msg.msg_controllen = CONTROL_LEN;

	int n = recvmsg(fd, &msg, 0);
    if(n==-1)
    {
        std::cout<<strerror(errno)<<"\n";
        exit(-1);
    }
    else if(n==0)
    {
        std::cout<<"父进程退出，其余进程不在处理\n";
        exit(0);
    }
	int fd_to_read = *(int*)CMSG_DATA(&cm);
	return fd_to_read;
}

    void PushTask(int sockfd)
    {
        send_fd(_proc.front()._writefd,sockfd);
        close(sockfd);
        close(_proc.front()._writefd);
        _proc.pop_front();
    }
    Processes(func_t func,int proc_num = 5)
    :_func(func)
    ,_proc_num(proc_num)
    {
        signal(SIGCHLD,SIG_IGN);
        std::vector<int> temp;
        for(int i = 0;i<_proc_num;i++)
        {
            int pipefd[2];
            int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
            assert(ret != -1);
            int pid = fork();
            if(pid <0)
            {
                close(pipefd[0]);
                close(pipefd[1]);
                i--;
                continue;
            }
            else if(pid==0)
            {
                //防止文件描述符泄露
                if (!temp.empty())
                {
                    for (auto fd : temp)
                    {
                        close(fd);
                    }
                }
                //关闭写端
                close(pipefd[1]);
                int sockfd = recv_fd(pipefd[0]);
                _func(sockfd);
                exit(0);
            }
            else 
            {
                //父进程
                _proc.push_back(proc_r(pid,pipefd[0],pipefd[1]));
                close(_proc.front()._readfd);
                temp.push_back(_proc.front()._writefd);
                continue;
            }
        }
    }
};